-- mysql

local resty_mysql = require('resty.mysql')
local resty_lru = require('resty.lrucache')

local ngx = ngx

local mis_log = nil
local mis_config = nil
local mis_qps = nil
local t_retCode = nil

--------------------------------------------------------------
local mis_mysql = {
	s_name = string.match(debug.getinfo(1).short_src, '.*/(.*).lua');	-- 模块名称
	t_lru = nil;														-- lru
--	t_mysql = nil;														-- mysql连接对象
--	s_conName = nil;													-- mysql连接名
}
local this = mis_mysql
this.__index = this

--------------------------------------------------------------
-- 说明：模块包含
-- 参数：模块表
this.modules_include = function(t_moddules)
	mis_log = t_moddules.mis_log
	mis_config = t_moddules.mis_config
	mis_qps = t_moddules.mis_qps
	t_retCode = mis_config.t_retCode
end

--------------------------------------------------------------
-- 说明：load级初始化
this.init_load = function()
	local t_lru, s_err = resty_lru.new(mis_config.n_mysqlLruSize)
	if not t_lru then
		s_err = 'failed to create lru, ' .. s_err
		mis_log.error(s_err)
		return t_retCode.n_SYS_ERR, s_err
	end
	this.t_lru = t_lru
	return t_retCode.n_OK
end

--------------------------------------------------------------
-- 说明：创建mysql连接
-- 参数：t_mysqlConf		数据库配置
-- 返回：n				状态码
--		ts 				描述
this.new = function(t_mysqlConf)
	local t_mysql, s_desc = resty_mysql:new()
	if not t_mysql then
		s_desc = 'failed to new mysql, ' .. s_desc
		mis_log.error(s_desc)
		return t_retCode.n_DB_ERR, s_desc
	end
	t_mysql:set_timeout(mis_config.t_mysql.n_timeOut)
	local b_ok, s_desc, n_code, s_sqlstate = t_mysql:connect({
		host = t_mysqlConf.s_host;
		port = t_mysqlConf.n_port;
		database = t_mysqlConf.s_database;
		user = t_mysqlConf.s_user;
		password = t_mysqlConf.s_password;
		charset = 'utf8';
	})
	if not b_ok then
		s_desc = 'failed to connect mysql, ' .. s_desc .. ', code=' .. (n_code or 'nil') .. ', sqlstate=' .. (s_sqlstate or 'nil')
		mis_log.error(s_desc)
		return t_retCode.n_DB_ERR, s_desc
	end
	local t_o = {
		t_mysql = t_mysql;
		s_conName = t_mysqlConf.s_host .. '_' .. t_mysqlConf.n_port;
	}
	setmetatable(t_o, this)
	return t_retCode.n_OK, t_o
end

--------------------------------------------------------------
-- 说明：mysql单次查询
-- 参数：t_mysqlConf		数据库配置
--		s_sql 			查询
--		s_key			缓存ID(nil代表不使用缓存)
-- 返回：n				状态码
--		ts 				描述
this.query_once = function(t_mysqlConf, s_sql, s_key)
	local n_code, ts_mysql = this.new(t_mysqlConf)
	if n_code ~= t_retCode.n_OK then
		return n_code, ts_mysql
	end
	local ts_res
	n_code, ts_res = ts_mysql:query(s_sql, s_key)
	ts_mysql:keepalive()
	return n_code, ts_res
end

--------------------------------------------------------------
-- 说明：mysql查询
-- 参数：s_sql 			查询
--		s_key			缓存ID(nil代表不使用缓存)
-- 返回：n				状态码
--		ts 				描述
function this:query(s_sql, s_key)
	if s_key then
		local t_data = self.t_lru:get(s_key)
		if t_data then return t_retCode.n_OK, t_data end
	end

	-- 过载保护
	local n_qps = mis_qps.mark(self.s_conName)
	if n_qps > mis_config.n_mysqlQueryLimit then
		local s_desc = 'mysql overloaded, name=' .. self.s_conName .. ', qps=' .. n_qps
		mis_log.warning(s_desc)
		return t_retCode.n_DB_LIMIT, s_desc
	end

	local t_data, s_desc, n_code, s_sqlstate = self.t_mysql:query(s_sql)
	if not t_data then
		s_desc = 'failed to query mysql, ' .. s_desc .. ', code=' .. (n_code or 'nil') .. ', sqlstate=' .. (s_sqlstate or 'nil') .. ', sql=' .. s_sql
		mis_log.error(s_desc)
		return t_retCode.n_DB_ERR, s_desc
	else
		if s_key then self.t_lru:set(s_key, t_data, mis_config.n_mysqlLruTtl) end
	end
	return t_retCode.n_OK, t_data
end

--------------------------------------------------------------
-- 说明：回收到mysql连接池
-- 返回：n			状态码
--		s 			描述
function this:keepalive()
	local b_ok, s_desc = self.t_mysql:set_keepalive(mis_config.t_mysql.n_idleTime, mis_config.t_mysql.n_poolSize)
	if not b_ok then
		s_desc = 'failed to keepalive mysql, ' .. s_desc
		mis_log.error(s_desc)
		return t_retCode.n_DB_ERR, s_desc
	end
	return t_retCode.n_OK
end

--------------------------------------------------------------
-- 说明：mysql关闭连接
-- 返回：n			状态码
--		s 			描述
function this:close()
	local b_ok, s_desc = self.t_mysql:close()
	if not b_ok then
		s_desc = 'failed to close mysql, ' .. s_desc
		mis_log.error(s_desc)
		return t_retCode.n_DB_ERR, s_desc
	end
	return t_retCode.n_OK
end

return this